package mgo

import (
	"context"
	"github.com/pkg/errors"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type TransactionOperationInfo struct {
	Client             *mongo.Client     // Mongodb 操作客户端
	Infos              []*TOperationInfo // 事务操作内容
	Ctx                context.Context   // 注入操作上下文，如果未传入，默认建立 context.Background()
	TransactionOptions []*options.TransactionOptions
}

type TOperationInfo struct {
	Info *OperationInfo // 单条操作信息
	Func string         // 操作方法名称，范围如下：InsertOne/InsertMany/UpdateByID/UpdateOne/UpdateMany/DeleteOne/DeleteMany
}

// Transaction 开启一个会话：sess = mgoClient.startSession()
// 开启事务：sess.startTransaction()
// 回滚：sess.abortTransaction()
// 提交：sess.commitTransaction()
// 结束会话：sess.endSession()
func Transaction(info *TransactionOperationInfo) ([]interface{}, error) {
	if info.Client == nil {
		return nil, errors.New("mongodb client cannot be nil")
	}
	if info.Infos == nil && len(info.Infos) == 0 {
		return nil, errors.New("operation info cannot be empty")
	}
	if info.Ctx == nil {
		info.Ctx = context.Background()
	}

	result := make([]interface{}, 0)
	err := info.Client.UseSession(info.Ctx, func(sessionContext mongo.SessionContext) error {
		// 1. 开启事务
		if err := sessionContext.StartTransaction(info.TransactionOptions...); err != nil {
			return err
		}
		defer sessionContext.EndSession(info.Ctx) // 最后的时候，关闭会话
		// 2. 遍历操作，执行任务
		for _, v := range info.Infos {
			v.Info.Ctx = info.Ctx
			res, err := Action(v)
			if err != nil {
				_ = sessionContext.AbortTransaction(sessionContext) // 出现错误，执行事务回退
				return err
			}
			result = append(result, res)
		}
		// 3. 提交事务
		_ = sessionContext.CommitTransaction(sessionContext)
		return nil
	})
	return result, err
}

func UpdateMany(info *TransactionOperationInfo) (*mongo.UpdateResult, error) {
	if info.Infos != nil {
		info.Infos[0].Func = "UpdateMany"
	}
	res, err := Transaction(info)
	result, ok := res[0].(*mongo.UpdateResult)
	if err != nil || !ok {
		return nil, err
	}
	return result, nil
}

func DeleteMany(info *TransactionOperationInfo) (int64, error) {
	if info.Infos != nil {
		info.Infos[0].Func = "DeleteMany"
	}
	res, err := Transaction(info)
	affected, ok := res[0].(int64)
	if err != nil || !ok {
		return 0, err
	}
	return affected, nil
}

// Action 执行事务种的单个操作方法
func Action(tInfo *TOperationInfo) (interface{}, error) {
	if tInfo.Func == "" {
		return nil, errors.New("operation func cannot be empty")
	}
	switch tInfo.Func {
	case "InsertOne":
		return InsertOne(tInfo.Info)
	case "InsertMany":
		return InsertMany(tInfo.Info)
	case "UpdateByID":
		return UpdateByID(tInfo.Info)
	case "UpdateOne":
		return UpdateOne(tInfo.Info)
	case "UpdateMany":
		// 如果 Filter 传入 bson.D{} 或 bson.M{}，代表修改当前集合的所有数据
		// 但是出于安全考虑，限制最多修改 100 条数据，更多的数据修改通过运维层面操作
		res, err := updateMany(tInfo.Info)
		if res.ModifiedCount > 100 {
			return nil, errors.New("the affected count of update more than 100, please check operation")
		}
		return res, err
	case "FindOneAndUpdate":
		return nil, FindOneAndUpdate(tInfo.Info)
	case "DeleteOne":
		return DeleteOne(tInfo.Info)
	case "DeleteMany":
		// 如果 Filter 传入 bson.D{} 或 bson.M{}，代表删除当前集合的所有数据
		// 但是出于安全考虑，限制最多删除 100 条数据，更多的数据删除通过运维层面操作
		affected, err := deleteMany(tInfo.Info)
		if affected > 100 {
			return nil, errors.New("the affected count of delete more than 100, please check operation")
		}
		return affected, err
	case "FindOneAndDelete":
		return nil, FindOneAndDelete(tInfo.Info)
	case "ReplaceOne":
		return ReplaceOne(tInfo.Info)
	case "FindOneAndReplace":
		return nil, FindOneAndReplace(tInfo.Info)
	default:
		return nil, errors.New("operation func is abnormal")
	}
}
